#include "ParserTableros.h"
#include <list>



ParserTableros::ParserTableros(Json::Value& tableros, int fil, int col) {
    this->tableros = &tableros;
    this->cantFil = fil;
    this->cantCol = col;

    fichasDesaparecidas = 0;
}

/** Descripcion: inicializa la matriz de enteros "tablerosCayendo". Indica
    huecos, fichas desaparecidas y fichas no desaparecidas. */
void ParserTableros::iniTableroFichasCayendo(Json::Value& tab_desapariciones) {
    int x,y;
    Json::Value ficha;
    for (int i = 0; i < cantFil; i++) {
        for (int j = 0; j < cantCol; j++) {
          /* Inicializa contador de candies a caer */
          tableroCayendo[i][j]=0;
        }
    }
    /* Cargo elementos fijos que no pueden caer */
    for (int i = 0; i < (int)tab_desapariciones.size(); i++) {
        ficha.clear();
        ficha = tab_desapariciones[i];
        x=ficha["x"].asInt();
        y=ficha["y"].asInt();

        if (ficha["candy"].asString() == HUECO) {
            tableroCayendo[x][y]=ELEMENTO_FIJO_A_SALTAR;
        } else if (ficha["candy"].asString() == DESOCUPADO){
            tableroCayendo[x][y]=DESAPARECIO;
            fichasDesaparecidas++;
        }
    }
}

/** Descripcion: carga una matriz con coeficientes enteros que indican cuántos
    casilleros debe caer la ficha (x,y).  */
void ParserTableros::cargarTableroFichasCayendo() {
    int bajar, f_elem_caer;
    bool elem_encontrado;
    for (int col=0; col<cantCol; col++){
        for (int fila=cantFil-1; fila>=0; fila--){
            /* Si es un elemento que desaparecio o uno que
               debe caer se hace caer al superior */
            if (tableroCayendo[fila][col]==DESAPARECIO ||
                tableroCayendo[fila][col]>0){
                bajar= 1;
                f_elem_caer=fila-1;
                elem_encontrado=false;
                while (f_elem_caer>=0 && !elem_encontrado){
                    if (tableroCayendo[f_elem_caer][col]!=ELEM_CAER){
                        f_elem_caer--;
                        bajar++;
                    } else {
                        tableroCayendo[f_elem_caer][col]=bajar;
                        elem_encontrado=true;
                    }
                }
            }
        }
    }
}

/** Descripcion: traduce a json la matriz obtenida en
    "cargarTableroFichasCayendo" */
Json::Value ParserTableros::tomarFichasQueCaeran() {
    Json::Value vecFichasCaer(Json::arrayValue);
    Json::Value fichaCaer;

    for (int i = 0; i < cantFil; i++) {
        for (int j = 0; j < cantCol; j++) {
            if (tableroCayendo[i][j] > 0) {
                fichaCaer.clear();
                fichaCaer["x"] = i;
                fichaCaer["y"] = j;
                fichaCaer["desplazamiento"] = tableroCayendo[i][j];
                fichaCaer["desplazamientoAux"] = tableroCayendo[i][j];
                vecFichasCaer.append(fichaCaer);
            }
        }
    }

    if (MODO_DEBUG==1){
        std::stringstream ss;
        ss << "<PARSER> FichasQueCaeran: " << std::endl
           << vecFichasCaer;
        Log::loguear(MDEBUG,ss.str());
    }
    return vecFichasCaer;
}

/** Descripcion: retorna una matriz en formato Json indicando los
    datos "x","y" y "desplazamiento". El ultimo indicará cuántas
    posiciones deberá bajar (por gravedad) la ficha ("x","y") */
Json::Value ParserTableros::obtenerFichasQueCaeran(Json::Value&
        vecFichasDesaparecer, Json::Value& tab_desapariciones) {
    iniTableroFichasCayendo(tab_desapariciones);

    cargarTableroFichasCayendo();

    return tomarFichasQueCaeran();
}

/** Descripcion: retorna en formato Json las fichas que desaparecieron
    luego de aplicar las reglas al tablero pasado por parametro. */
Json::Value ParserTableros::obtenerFichasQueDesaparecieron(
    Json::Value& tablero) {

    Json::Value vecFichasDesaparecer(Json::arrayValue);
    Json::Value ficha;
    Json::Value fichaDesaparecer;
    for (int i = tablero.size()-1; i>=0; i--) {
        fichaDesaparecer.clear();
        ficha.clear();
        ficha = tablero[i];
        /* Si la ficha fue reemplazada por una minibar o goldenstar
           desaparece y aparece esta */
        fichaDesaparecer["magica"] = ((!ficha["es_magico"].isNull()) &&
                                        ficha["es_magico"].asBool()==true);
        if (ficha["candy"].asString() == DESOCUPADO ||
            fichaDesaparecer["magica"].asBool()==true) {
            fichaDesaparecer["x"] = ficha["x"].asInt();
            fichaDesaparecer["y"] = ficha["y"].asInt();
            if (fichaDesaparecer["magica"].asBool()){
              /* Indico a la animacion en cuál candy se convirtió */
              fichaDesaparecer["candy"] = ficha["candy"].asString();
              fichaDesaparecer["color"] = ficha["color"].asString();
            }

            vecFichasDesaparecer.append(fichaDesaparecer);
        }
    }

    if (MODO_DEBUG==1){
        std::stringstream ss;
        ss << "<PARSER> FichasDesaparecer: " << std::endl
           << vecFichasDesaparecer;
        Log::loguear(MDEBUG,ss.str());
    }
    return vecFichasDesaparecer;
}

/** Descripcion: retorna las fichas aleatorias en formato Json. Recibe como
    parametros el tablero de gravedad (el 2do enviado por servidor) y el
    tablero de fichas aleatorias (el 3ro enviado por servidor) y compara
    ambos para obtener el resultado. */
Json::Value ParserTableros::obtenerFichasRamdom(Json::Value& tableroGravedad,
        Json::Value& tableroRamdon) {
    Json::Value vecFichasRamdom(Json::arrayValue);
    Json::Value ficha, fichaRamdon;

    for (int i = 0; i < (int)tableroGravedad.size(); i++) {
        ficha.clear();
        fichaRamdon.clear();
        ficha = tableroGravedad[i];
        if (ficha["candy"].asString() == DESOCUPADO) {
            fichaRamdon = tableroRamdon[i];
            fichasDesaparecidas--;

            vecFichasRamdom.append(fichaRamdon);
        }
        if (fichasDesaparecidas == 0) {
            break;
        }
    }

    if (MODO_DEBUG==1){
        std::stringstream ss;
        ss << "<PARSER> FichasAleatorias: " << std::endl
           << vecFichasRamdom;
        Log::loguear(MDEBUG,ss.str());
    }
    return vecFichasRamdom;
}

std::list<Json::Value> *ParserTableros::obtenerAnimaciones() {
    int cantidadDeTableros = tableros->size();
    int tableroParseado = 0;
    std::list<Json::Value> *animaciones = new std::list<Json::Value>;

    Json::Value vecFichasDesaparecer , vecFichasRamdom, vecFichasCaer;

    while (cantidadDeTableros != tableroParseado) {
        vecFichasDesaparecer.clear();
        vecFichasRamdom.clear();
        vecFichasCaer.clear();

        vecFichasDesaparecer = obtenerFichasQueDesaparecieron(
                                   (*tableros)[tableroParseado]);

        /* Procesa tablero nro. 1: desapariciones */
        animaciones->push_back(vecFichasDesaparecer);

        vecFichasCaer = obtenerFichasQueCaeran(vecFichasDesaparecer,
                                               (*tableros)[tableroParseado]);

        animaciones->push_back(vecFichasCaer);

        /* Procesa tablero nro. 2: gravedad */
        tableroParseado++;
        Json::Value& tableroGravedad = (*tableros)[tableroParseado];

        /* Procesa tablero nro. 3: aleatorias */
        tableroParseado++;
        Json::Value& tableroRamdon = (*tableros)[tableroParseado];

        /* Obtiene fichas aleatorias a partir de los ultimos dos tableros. */
        vecFichasRamdom = obtenerFichasRamdom(tableroGravedad, tableroRamdon);
        animaciones->push_back(vecFichasRamdom);
        tableroParseado++;
    }

    return animaciones;
}

ParserTableros::~ParserTableros() { }
